import Event from './Event';
import Debug from './tools/Debug';
import * as THREE from 'three';
import { loadByUrl } from './tools/loader';
import ResourceTracker from './tools/ResourceTracker';
import { Plugins } from './PluginManager';

interface BaseModelEvents {
  [BaseModel.EVENT_BASEMODEL_LOADED]: (model: THREE.Object3D | null) => void;
  [BaseModel.EVENT_BASEMODEL_SCALE_CHANGED]: (scale: Editor.BaseModelScale) => void;
  [BaseModel.EVENT_BASEMODEL_PLUGINS_LOADED]: (plugins: Plugins) => void;
}

/**
 * 基础模型类，提供模型加载、缩放和事件系统
 */
export default class BaseModel extends Event<BaseModelEvents> {
  /** 模型加载回调，失败时返回null，成功返回模型 */
  static EVENT_BASEMODEL_LOADED = 'EVENT_BASEMODEL_LOADED';
  /** 模型缩放 返回scale*/
  static EVENT_BASEMODEL_SCALE_CHANGED = 'EVENT_BASEMODEL_SCALE_CHANGED';
  /** 模型插件加载完成 返回插件record */
  static EVENT_BASEMODEL_PLUGINS_LOADED = 'EVENT_BASEMODEL_PLUGINS_LOADED';

  private readonly _namespace = 'BaseModel';

  /**
   * 模型对象
   */
  model: THREE.Object3D | null = null;

  /**
   * 基础模型选项
   */
  baseModelOption: Editor.BaseModelOption = {
    url: '',
    scene: null,
  };

  /**
   * 缩放设置
   */
  scale: Editor.BaseModelScale = {
    base: 0.5,
    x: 1,
    y: 1,
    z: 1,
  };

  /**
   * 资源跟踪器，用于管理 THREE.js 对象的生命周期
   */
  resourceTracker: ResourceTracker | null = null;

  /**
   * @param {Editor.BaseModelOption} option
   */
  constructor(option: Editor.BaseModelOption) {
    super();
    const { autoLoad = true } = option;
    if (autoLoad) {
      this.init(option);
      this.baseModelOption = option;
    } else {
      this.baseModelOption = option;
    }
  }

  /**
   * @param {Editor.BaseModelOption} option
   * @returns {Promise<THREE.Object3D| null>}
   */
  init(option: Editor.BaseModelOption = this.baseModelOption): Promise<THREE.Object3D | null> {
    return new Promise((resolve, reject) => {
      const { url, scene, scale = this.scale } = option;
      this.scale = Object.assign(this.scale, scale);
      loadByUrl(url)
        .then((model) => {
          this.resourceTracker = new ResourceTracker();
          this.resourceTracker.track(model);
          this.model = model;
          this.changeScale(this.scale);
          scene.add(model);
          Debug.log(this._namespace, '模型加载成功', model);
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, model);
          resolve(model);
        })
        .catch((e) => {
          Debug.error(this._namespace, '模型加载错误', e);
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, null);
          reject(null);
        });
    });
  }

  // 变更模型url
  changeUrl(url: string) {
    return new Promise((resolve, reject) => {
      if (typeof url !== 'string') return;
      const { scene } = this.baseModelOption;
      loadByUrl(url)
        .then((model) => {
          // 销毁上一个模型
          this.baseModelDispose();
          this.resourceTracker = new ResourceTracker();
          this.resourceTracker.track(model);
          this.model = model;
          this.changeScale(this.scale);
          Debug.log(this._namespace, '-changeUrl-模型加载成功', model);
          scene.add(model);
          // 保存url到option
          this.baseModelOption.url = url;
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, model);
          resolve(model);
        })
        .catch((e) => {
          Debug.error(this._namespace, '-changeUrl-模型加载错误', e);
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, null);
          reject(null);
        });
    });
  }

  /**
   * @description 改变缩放尺寸
   * @param {object} scaleOption
   * @param {number} [scaleOption.base] 基础缩放
   * @param {number} [scaleOption.x] x轴缩放
   * @param {number} [scaleOption.y] y轴缩放
   * @param {number} [scaleOption.z] z轴缩放
   */
  changeScale(scaleOption: { base?: number; x?: number; y?: number; z?: number }): void {
    if (!this.model || !scaleOption) return;
    // 合并scaleOption和this.scal
    this.scale = Object.assign(this.scale, scaleOption);
    // 设置缩放
    const { base, x, y, z } = this.scale;
    this.model.scale.set(x * base, y * base, z * base);
    Debug.log(this._namespace, '-changeScale-模型缩放成功', this.scale);
    this._emitEvent(BaseModel.EVENT_BASEMODEL_SCALE_CHANGED, this.scale);
  }

  /**
   * FIXME: 后续考虑优化，当没有调用changeScale时，直接返回cache
   * @description 获取模型信息，需要注意这里返回的size需要乘以缩放的倍数的倒数才是真实值
   * @returns {{center: THREE.Vector3, size: THREE.Vector3, basePoint: THREE.Vector3}}
   */
  getInfo(): {
    center: THREE.Vector3;
    size: THREE.Vector3;
    basePoint: THREE.Vector3;
  } | null {
    if (!this.model) return null;
    const box = new THREE.Box3().setFromObject(this.model);
    const center = box.getCenter(new THREE.Vector3());
    const size = box.getSize(new THREE.Vector3());

    return {
      basePoint: this.model.position.clone(),
      center,
      size,
    };
  }

  /**
   *  @description 将传入的object基准点与baseModel基准点对齐
   */
  followBasePoint(object: THREE.Mesh | THREE.Object3D | THREE.Group): void {
    if (!this.model || !object) return;
    object.position.copy(this.model.position);
  }

  /**
   * @description 将传入的object的旋转与baseModel对齐
   */
  followQuaternion(object: THREE.Mesh | THREE.Object3D | THREE.Group): void {
    if (!this.model || !object) return;
    object.quaternion.copy(this.model.quaternion);
  }

  /**
   * @description 销毁baseModel
   */
  baseModelDispose(): void {
    if (this.model) {
      const { scene } = this.baseModelOption;
      if (scene && this.model.parent === scene) {
        scene.remove(this.model);
      }
      // 使用 ResourceTracker 释放所有跟踪的资源
      this.resourceTracker?.dispose();
      this.resourceTracker = null;
      this.model = null;
    }
  }
}
